import 'package:dio/dio.dart';
import 'package:flutter_enjoy_plus/constants/index.dart';
import 'package:flutter_enjoy_plus/utils/Emitter.dart';
import 'package:flutter_enjoy_plus/utils/TokenManager.dart';
import 'package:flutter_enjoy_plus/utils/toast.dart';

class RequestDio {
  final Dio _dio = Dio();
  RequestDio() {
    // _dio.options.baseUrl = GlobalVariable.BASE_URL;
    // _dio.options.connectTimeout =
    //     const Duration(seconds: GlobalVariable.TIMEOUT);
    // _dio.options.receiveTimeout =
    //     const Duration(seconds: GlobalVariable.TIMEOUT);
    //? 针对一个对象赋值可以用..
    _dio
      ..options.baseUrl = GlobalVariable.BASE_URL
      ..options.connectTimeout = const Duration(seconds: GlobalVariable.TIMEOUT)
      ..options.receiveTimeout =
          const Duration(seconds: GlobalVariable.TIMEOUT);
    _registerIntercepors();
  }
  //? 封装求情方法
  //? get请求
  Future get(String url, {Map<String, dynamic>? params}) async {
    final result = await _dio.get(url, queryParameters: params);
    return _handleResult(result);
  }

  //? post请求
  Future post(String url, {Map<String, dynamic>? data}) async {
    final result = await _dio.post(url, data: data);
    return _handleResult(result);
  }

  //? put请求
  Future put(String url, {Map<String, dynamic>? data}) async {
    final result = await _dio.put(url, data: data);
    return _handleResult(result);
  }

  //? delete请求
  Future delete(String url, {Map<String, dynamic>? data}) async {
    final result = await _dio.delete(url, data: data);
    return _handleResult(result);
  }

  //? 针对所有请求-统一返回结果
  _handleResult(Response<dynamic> response) {
    if (response.data['code'] == GlobalVariable.CODE_SUCCESS) {
      //? 请求业务成功
      return response.data['data'];
    }
    PromptAction.showError(response.data["message"]);
    //? 请求业务失败 -> 抛出异常（程序员）/增加提示（用户）
    throw Exception(response.data['message']);
  }

  // 判断refreshToken是否过期
  Future<bool> _isRefreshTokenExpired() async {
    // 1. 获取刷新token
    // 2. 发请求 -> /refreshToken
    // 3. 该请求中传入refreshToken
    // 4. 根据状态码 来判断token是否过期
    final refreshToken = tokenManager.getRefreshToken();
    // 注意点: 自己创建一个dio实例 -> 为了避免使用之前的dio实例发请求 (携带的token -> 过期的)
    Dio dio = Dio();
    try {
      final res = await dio.post(
          '${GlobalVariable.BASE_URL}${HTTP_PATH.REFRESH_TOKEN}',
          options: Options(headers: {'Authorization': 'Bearer $refreshToken'}));
      if (res.data['code'] == 10000) {
        // 刷新token成功了
        // 将获取到的最新的token和refreshToken 进行存储
        tokenManager.setToken(res.data['data']['token'],
            refreshToken: res.data['data']['refreshToken']);
        return true;
      } else {
        return false;
      }
    } catch (e) {
      return false;
    }
  }

  _registerIntercepors() {
    _dio.interceptors.add(
      InterceptorsWrapper(
          //? 请求拦截器
          onRequest:
              (RequestOptions options, RequestInterceptorHandler handler) {
        //? 如果你想完成请求并返回一些自定义数据，你可以使用`handler.resolve(result)`方法。
        //? 如果你想 终止请求并返回一个自定义的响应数据，你可以使用`handler.reject(error)`方法。
        //? 1、添加token
        //? 2、如果有token，携带
        final token = tokenManager.getToken();
        if (token.isNotEmpty) {
          options.headers['Authorization'] = 'Bearer $token';
        }
        return handler.next(options);
        // request里面是请求参数
        return handler.next(options);
      },
          //? 响应拦截器
          onResponse: (Response response, ResponseInterceptorHandler handler) {
        //? http业务状态码 200 -300 之间是成功的
        if (response.statusCode! >= 200 && response.statusCode! < 300) {
          return handler.next(response); // 处理数据  这里可以处理错误
        } else {
          //? 加401判断
          //? 4开头（客户端异常->请求路径 错误/鉴权 （token）） 5开头是服务端异常
          //? 针对于401授权失败的情况
          //? 提示用户
          PromptAction.showError(response.statusMessage!); // 处理http 请求的问题
          return handler
              .reject(DioException(requestOptions: response.requestOptions));
        }
      },
          //? 异常处理
          onError: (DioException error, ErrorInterceptorHandler handler) async {
        //? 401授权失败的逻辑
        if (error.response?.statusCode == 401) {
          // 考虑刷新token
          // 1. 获取refreshToken
          // 2. 判断refreshToken 是否存在存在
          // 2.2 如果存在的情况下 继续判断refreshToken 是否过期
          // 2.3 如何判定refreshToken 是否过期 -> 取决于 拿着刷新token 请求接口的时候, 判断返回状态码
          // 2.4 如果过期  - 清除本地缓存 +  跳转到登录页

          final refreshToken = tokenManager.getRefreshToken();
          if (refreshToken.isNotEmpty) {
            // 刷新token存在的情况 ??????
            // 是否过期 封装方法 -> 返回布尔值 true -> 没有过期 / 返回false 过期
            bool isOk = await _isRefreshTokenExpired();
            if (isOk) {
              //? refreshToken有效->存储新token和新refreshToken 成功了
              final res = await _dio.fetch(error.requestOptions);
              return handler.resolve(res);
            } else {
              tokenManager.deleteToken();
              eventBus.fire(LogoutEvent());
            }
          } else {
            // 刷新token不存在的情况
            // 清除本地缓存
            // 跳转到登录页
            tokenManager.deleteToken();
            eventBus.fire(LogoutEvent());
          }
        }
        return handler.next(error);
      }),
    );
  }
}

final requestDio = RequestDio();
